package logic

import (
	"common"
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/models/entity"
	"core/repo"
	"core/service"
	"fmt"
	"framework/msError"
	"framework/remote"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"hall/component/proto"
	"hall/models/request"
	"strconv"
	"sync"
	"time"
)

const (
	MaxMarketWeb      = 10 * 60 // 用户访问时间心跳 =
	MaxMarketOrderPay = 10 * 60 // 订单可操作时间
)

type MarketManager struct {
	sync.RWMutex
	shipUsers              map[uint]*entity.User
	shipUsersSchedules     map[uint]*time.Timer
	buildUsers             map[uint]*entity.User
	buildUsersSchedules    map[uint]*time.Timer
	materialUsers          map[uint]*entity.User
	materialUsersSchedules map[uint]*time.Timer
	orderCh                chan *proto.MarketBuy
	schedules              map[primitive.ObjectID]*time.Timer
	marketService          *service.MarketService
	userService            *service.UserService
	walletService          *service.WalletService
	warehouseService       *service.WarehouseService
	manager                *Manager
}

func (m *MarketManager) run() {
	//订单处理
	go m.orderChHandler()
	//重启任务，未支付订单自动退回
	logs.Warn("初始化重启定时任务-市场-超时取消")
	go m.restartScheduleExecute()
}

// UserWrite 写入数据
func (m *MarketManager) UserWrite(types int, user *entity.User) {
	m.Lock()
	defer m.Unlock()
	switch types {
	case 1:
		_, ok := m.shipUsers[user.Uid]
		if !ok {
			m.shipUsers[user.Uid] = user
			go m.scheduleShipUserExecute(user)
		} else {
			//TODO 重置心跳
			m.shipUsersSchedules[user.Uid].Reset(MaxGoodsWeb * time.Second)
		}
	case 2:
		_, ok := m.buildUsers[user.Uid]
		if !ok {
			m.buildUsers[user.Uid] = user
			go m.scheduleBuildUserExecute(user)
		} else {
			//TODO 重置心跳
			m.buildUsersSchedules[user.Uid].Reset(MaxGoodsWeb * time.Second)
		}
	case 3:
		_, ok := m.materialUsers[user.Uid]
		if !ok {
			m.materialUsers[user.Uid] = user
			go m.scheduleMaterialUsersExecute(user)
		} else {
			//TODO 重置心跳
			m.materialUsersSchedules[user.Uid].Reset(MaxGoodsWeb * time.Second)
		}
	}
}

// scheduleShipUserExecute 心跳 定时删除用户组里的用户数据
func (m *MarketManager) scheduleShipUserExecute(user *entity.User) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	t, ok := m.shipUsersSchedules[user.Uid]
	if ok {
		t.Stop()
		delete(m.shipUsersSchedules, user.Uid)
	}
	m.shipUsersSchedules[user.Uid] = time.AfterFunc(time.Second*MaxMarketWeb, func() {
		// 无更新心跳 关闭定时任务
		timer, ok := m.shipUsersSchedules[user.Uid]
		if ok {
			timer.Stop()
		}
		delete(m.shipUsersSchedules, user.Uid)
		// 踢出用户
		delete(m.shipUsers, user.Uid)
	})
}

// scheduleBuildUserExecute 心跳 定时删除用户组里的用户数据
func (m *MarketManager) scheduleBuildUserExecute(user *entity.User) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	t, ok := m.buildUsersSchedules[user.Uid]
	if ok {
		t.Stop()
		delete(m.buildUsersSchedules, user.Uid)
	}
	m.buildUsersSchedules[user.Uid] = time.AfterFunc(time.Second*MaxMarketWeb, func() {
		// 无更新心跳 关闭定时任务
		timer, ok := m.buildUsersSchedules[user.Uid]
		if ok {
			timer.Stop()
		}
		delete(m.buildUsersSchedules, user.Uid)
		// 踢出用户
		delete(m.buildUsers, user.Uid)
	})
}

// scheduleMaterialUsersExecute 心跳 定时删除用户组里的用户数据
func (m *MarketManager) scheduleMaterialUsersExecute(user *entity.User) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	t, ok := m.materialUsersSchedules[user.Uid]
	if ok {
		t.Stop()
		delete(m.materialUsersSchedules, user.Uid)
	}
	m.materialUsersSchedules[user.Uid] = time.AfterFunc(time.Second*MaxMarketWeb, func() {
		// 无更新心跳 关闭定时任务
		timer, ok := m.materialUsersSchedules[user.Uid]
		if ok {
			timer.Stop()
		}
		delete(m.materialUsersSchedules, user.Uid)
		// 踢出用户
		delete(m.materialUsers, user.Uid)
	})
}

func (m *MarketManager) orderChHandler() {
	for {
		select {
		case data, ok := <-m.orderCh:
			if ok {
				ctx := context.TODO()
				//4.创建订单并扣除数量
				var err *msError.Error
				uidStr := []string{fmt.Sprintf("%d", data.MarketOrder.UID)}
				data.MarketOrder, err = m.marketService.MarketOrderGen(ctx, data.MarketOrder)
				if err != nil {
					m.manager.ServerMessagePush(uidStr, proto.AbnormalNotifyData(common.F(err)), data.Session)
					continue
				}
				m.manager.ServerMessagePush(uidStr, proto.AbnormalNotifyData(common.S("success")), data.Session)
				data.MarketOrder = m.marketService.OrderInfoByOrderNo(ctx, data.MarketOrder.UID, data.MarketOrder.OrderNo)
				if data.MarketOrder.SellTypeID == 0 {
					//5.创建定时任务未支付自动取消
					go m.scheduleExecute(data.MarketOrder, MaxOrderPayCancel, data.Session)
					//6.告诉用户订单已生成
					m.manager.ServerMessagePush(uidStr, proto.MarketNotifyData(data.MarketOrder), data.Session)
				}
			}
		}
	}
}

func (m *MarketManager) CreateOrder(ctx context.Context, uid uint, req *request.MarketBuy, session *remote.Session) (*entity.MarketOrder, *msError.Error) {
	//1.查询订单
	order := m.marketService.MarketOrderInfo(ctx, MaxMarketOrderPay, uid, req.ID)
	if order.OrderNo == "" {
		//2.查询售卖列表
		g := m.marketService.GetTrade(ctx, req.ID)
		if g == nil || g.ID == 0 {
			logs.Error("[marketHandler]用户%d MarketBuy err:获取商品数据失败 req=%v", uid, req)
			return nil, biz.GoodsError
		}
		num := req.Num
		if g.TypeID == 11 {
			num = utils.DecimalFloatByInt(utils.DecimalFloatMul(utils.DecimalIntByFloat(req.Num), utils.DecimalIntByFloat(g.Types.Val)))
		}
		if g.SyNum < num {
			logs.Error("[marketHandler]用户%d MarketBuy err:购买数量%d，大于%d req=%v", uid, g.SyNum, req.Num, g)
			return nil, biz.BuyNotNumError
		}
		//4.验证材料余额是否充足
		totalPrice := utils.DecimalFloatMul(g.Price, utils.DecimalIntByFloat(num))
		if g.SellTypeID != 0 {
			material := m.warehouseService.GetMaterial(ctx, uid, g.SellTypeID)
			if material.Num < utils.DecimalFloatByInt(totalPrice) {
				logs.Error("[marketHandler]用户%d MarketBuy err:材料余额不足 req=%v", uid, req)
				return nil, biz.GoodsPayError
			}
		}
		//3.向orderCh写入订单
		sellOrder := new(proto.MarketBuy)

		// TODO 查看用户是否有会员权限,有责获取会员手续费
		info := m.userService.GetUserOfRole(ctx, uid)
		if info.TransactionPassword == "" {
			logs.Error("[marketHandler]用户%d MarketBuy err:交易密码为空 req=%v", uid, req)
			return nil, biz.TransactionPasswordEmpty
		}
		if info.TransactionPassword != utils.Md5Password(req.Pwd) {
			logs.Error("[marketHandler]用户%d MarketBuy err:交易密码错误 req=%v", uid, req)
			return nil, biz.TransactionPasswordError
		}
		var confFee float64
		if info.Role.IsVIP {
			confFee = m.marketService.GetConfInfo(ctx, "member_trade_fee")
		} else if m.warehouseService.CardBuyCount(ctx, g.UID) > 0 {
			confFee = m.marketService.GetConfInfo(ctx, "card_trade_fee")
		} else {
			confFee = m.marketService.GetConfInfo(ctx, "trade_fee")
		}
		fee := utils.DecimalFloatMul(totalPrice, confFee)
		if g.SellTypeID != 11 {
			if fee < 0.01 {
				fee = 0.01
			}
		}
		if totalPrice == fee {
			fee = 0
		}
		sellOrder.MarketOrder = &entity.MarketOrder{
			TradeID:      g.ID,
			UID:          uid,
			SellUID:      g.UID,
			OrderNo:      utils.GenOrderNumber("Market"),
			TypeID:       g.TypeID,
			TypeName:     g.Types.Name,
			TypeImg:      g.Types.Img,
			TypeType:     g.Types.Type,
			SellTypeID:   g.SellTypeID,
			SellTypeName: g.SellTypes.Name,
			SellTypeImg:  g.SellTypes.Img,
			Num:          num,
			Price:        g.Price,
			TotalPrice:   totalPrice,
			Fee:          fee,
			PayStatus:    entity.TradePayNone,
			PayTime:      0,
			PayNotify:    "",
			Cancel:       0,
			IsHandle:     false,
			HandleTime:   0,
			CreateTime:   time.Now().Unix(),
		}
		sellOrder.Session = session
		m.orderCh <- sellOrder
	}
	return order, nil
}

func (m *MarketManager) scheduleExecute(order *entity.MarketOrder, times int64, session *remote.Session) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	if m.schedules[order.Id] != nil {
		m.schedules[order.Id].Stop()
	}
	m.schedules[order.Id] = time.AfterFunc(time.Second*time.Duration(times), func() {
		// 取消定时任务
		timer, ok := m.schedules[order.Id]
		if ok {
			timer.Stop()
		}
		var err error
		ctx := context.TODO()
		//var isQuxiao bool
		//delete(m.schedules, order.Id)
		//if order.SellTypeID == 0 {
		//	orderData := order.OrderNo[6:14]
		//	var hfResult *hall.ReturnResAsynchronous
		//	hfResult, err = m.walletService.OrderQueryWallet(order.OrderNo, orderData)
		//	if err != nil {
		//		logs.Error("goods manager 自动取消,err=%v", err)
		//	}
		//	if hfResult.TransStat == "S" {
		//		response, _ := json.Marshal(hfResult)
		//		err = m.marketService.TradePay(ctx, order, string(response))
		//		if order == nil || order.UID == 0 {
		//			logs.Error("trade manager 自动取消 完成失败,err=%v", err)
		//			return
		//		}
		//		order.PayStatus = entity.TradePay
		//		order.PayTime = time.Now().Unix()
		//		order.PayNotify = string(response)
		//	} else {
		//		isQuxiao = true
		//	}
		//} else {
		//	isQuxiao = true
		//}
		//if isQuxiao {
		//取消订单
		err = m.marketService.MarketOrderCancel(ctx, order)
		if err != nil {
			logs.Error("order timer scheduleExecute,err=%v", err)
			return
		}
		//}
		//发送订单消息
		if session != nil {
			m.manager.ServerMessagePush([]string{strconv.Itoa(int(order.UID))}, proto.MarketNotifyData(order), session)
		}
	})
}

func (m *MarketManager) OrderPay(uid uint, orderNo string) (string, *msError.Error) {
	//1.查询订单
	ctx := context.TODO()
	order := m.marketService.OrderInfoByOrderNo(ctx, uid, orderNo)
	if order == nil || order.OrderNo == "" {
		logs.Error("[marketHandler]用户%d MarketPay err:获取订单数据失败 req=%v", uid, order)
		return "", biz.GoodsOrderError
	}
	times := time.Now().Unix()
	if times > order.CreateTime+MaxMarketOrderPay {
		logs.Error("[marketHandler]用户%d MarketPay err:订单已超时,订单时间为%v,超时时间为%v req=%v", uid, utils.TimeOrDateAsYear(times, "", "YmdHis"), utils.TimeOrDateAsYear(order.CreateTime+MaxMarketOrderPay, "", "YmdHis"), order)
		return "", biz.GoodsOrderError
	}
	// TODO 调取支付,获取支付地址
	wallet := m.userService.GetWalletOfReal(ctx, uid)
	if wallet == nil || wallet.ID == 0 {
		logs.Error("[marketHandler]用户%d MarketPay err:获取钱包数据失败 req=%v", uid, wallet)
		return "", biz.WalletNotError
	}
	expTime := order.CreateTime + MaxOrderPay - time.Now().Unix()
	resPay, err := m.walletService.MarketPay(order.TotalPrice, orderNo, wallet.UserCustID, wallet.IDCard, wallet.RealName.Name, wallet.RealName.IDCard, int(expTime))
	if err != nil {
		logs.Error("[marketHandler]用户%d MarketPay err:调取支付失败 req=%v,payErr:%v", uid, wallet, err)
		return "", biz.NotPayError
	}
	return resPay, nil
}

func (m *MarketManager) restartScheduleExecute() {
	ctx := context.TODO()
	market := m.marketService.MarketBuyList(ctx)
	for k, v := range *market {
		data := v
		var times int64
		if time.Now().Unix() >= data.CreateTime+MaxOrderPayCancel {
			//已超时，未处理
			times = int64(k)
		} else {
			times = data.CreateTime + MaxOrderPayCancel - time.Now().Unix()
		}
		m.scheduleExecute(&data, times, nil)
	}

}

func NewMarketManager(r *repo.Manager) *MarketManager {
	m := &MarketManager{
		shipUsers:              make(map[uint]*entity.User),
		buildUsers:             make(map[uint]*entity.User),
		materialUsers:          make(map[uint]*entity.User),
		shipUsersSchedules:     make(map[uint]*time.Timer),
		buildUsersSchedules:    make(map[uint]*time.Timer),
		materialUsersSchedules: make(map[uint]*time.Timer),
		orderCh:                make(chan *proto.MarketBuy, 1024),
		schedules:              make(map[primitive.ObjectID]*time.Timer),
		marketService:          service.NewMarketService(r),
		userService:            service.NewUserService(r),
		walletService:          service.NewWalletService(r),
		warehouseService:       service.NewWarehouseService(r),
		manager:                NewManager(),
	}
	m.run()
	return m
}
